package org.zhang.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.zhang.shortlink.admin.common.constant.RedisConstant;
import org.zhang.shortlink.admin.common.constant.UserConstant;
import org.zhang.shortlink.admin.common.enums.UserErrorCodeEnum;
import org.zhang.shortlink.admin.dao.entity.SysUser;
import org.zhang.shortlink.admin.dao.mapper.SysUserMapper;
import org.zhang.shortlink.admin.dto.req.UserLoginDTO;
import org.zhang.shortlink.admin.dto.req.UserRegisterDTO;
import org.zhang.shortlink.admin.dto.req.UserUpdateDTO;
import org.zhang.shortlink.admin.dto.resp.SysUserVo;
import org.zhang.shortlink.admin.service.GroupService;
import org.zhang.shortlink.admin.service.SysUserService;
import org.zhang.shortlink.common.convention.exception.ClientException;

import java.util.concurrent.TimeUnit;

/**
 * @author zhang
 * @description 针对表【sys_user】的数据库操作Service实现
 * @createDate 2024-03-17 21:02:06
 */
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
        implements SysUserService {

    private final RBloomFilter<String> userRegisterCachePenetrationBloomFilter;

    private final RedissonClient redissonClient;

    private final StringRedisTemplate stringRedisTemplate;

    private final GroupService groupService;

    /**
     * 根据用户名获取用户信息
     *
     * @param username
     * @return
     */
    @Override
    public SysUserVo getUserByUsername(String username) {
        SysUser sysUser = baseMapper.selectOne(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getUsername, username));
        if (sysUser == null) {
            throw new ClientException(UserErrorCodeEnum.USER_NULL);
        }
        SysUserVo sysUserVo = new SysUserVo();
        BeanUtils.copyProperties(sysUser, sysUserVo);
        return sysUserVo;
    }

    /**
     * 判断用户名是否存在
     *
     * @param username
     * @return
     */
    @Override
    public Boolean hasUsername(String username) {
        return userRegisterCachePenetrationBloomFilter.contains(username);
    }

    /**
     * 用户注册
     *
     * @param userRegisterDTO
     */
    @Override
    public void register(UserRegisterDTO userRegisterDTO) {
        if (this.hasUsername(userRegisterDTO.getUsername())) {
            throw new ClientException(UserErrorCodeEnum.USER_NAME_EXIST);
        }
        RLock lock = redissonClient.getLock(UserConstant.LOCK_USER_REGISTER_KEY + userRegisterDTO.getUsername());
        try {
            if (lock.tryLock()) {
                int ret = baseMapper.insert(BeanUtil.toBean(userRegisterDTO, SysUser.class));
                if (ret < 1) {
                    throw new ClientException(UserErrorCodeEnum.USER_SAVE_ERROR);
                }
                userRegisterCachePenetrationBloomFilter.add(userRegisterDTO.getUsername());
                //创建默认分组
                groupService.saveGroup(userRegisterDTO.getUsername(),"默认分组");
            } else {
                throw new ClientException(UserErrorCodeEnum.USER_NAME_EXIST);
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 用户信息修改
     *
     * @param userUpdateDTO
     */
    @Override
    public void update(UserUpdateDTO userUpdateDTO) {
        //TODO 查询修改用户是否为当前登录用户
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getUsername, userUpdateDTO.getUsername());
        int update = baseMapper.update(BeanUtil.toBean(userUpdateDTO, SysUser.class), queryWrapper);
        if (update < 1) {
            throw new ClientException(UserErrorCodeEnum.USER_UPDATE_ERROR);
        }
    }

    /**
     * 登录接口
     * @param userLoginDTO
     * @return
     */
    @Override
    public String login(UserLoginDTO userLoginDTO) {
        //1、参数校验
        if (userLoginDTO == null) {
            throw new ClientException("登录接口参数为空");
        }
        String username = userLoginDTO.getUsername();
        String password = userLoginDTO.getPassword();

        //2、判断用户是否存在 TODO 不清楚布隆过滤器被误判的值是否一直会被误判
        if (!this.hasUsername(username)) {
            throw new ClientException(UserErrorCodeEnum.USER_NULL);
        }

        //3、判断是否登录
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisConstant.USER_LOGIN + username))) {
            throw new ClientException("用户已经登录");
        }

        //4、判断用户信息
        SysUser sysUser = baseMapper.selectOne(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getUsername, username)
                .eq(SysUser::getPassword, password));
        if (sysUser == null) {
            throw new ClientException("用户名或密码错误");
        }

        //5、成功登录保存信息到Redis
        String uuid = UUID.randomUUID().toString();
        String jsonStr = JSONUtil.toJsonStr(sysUser);
        stringRedisTemplate.opsForHash().put(RedisConstant.USER_LOGIN + username, uuid, jsonStr);
        stringRedisTemplate.expire(RedisConstant.USER_LOGIN + username, 30L, TimeUnit.MINUTES);

        return uuid;
    }

    /**
     * 用户登出
     *
     * @param username
     * @return
     */
    @Override
    public Void logout(String username) {
        //1、判断是否登录
        if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(RedisConstant.USER_LOGIN + username))) {
            throw new ClientException("用户未登录");
        }
        //2、登出
        stringRedisTemplate.delete(RedisConstant.USER_LOGIN + username);
        return null;
    }


}




